﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics.CodeAnalysis;

namespace Layers
{
    public class ListWrapper<T> : IList<T>
    {
        protected IList<T> _impl;

        public ListWrapper(IList<T> impl)
        {
            _impl = impl;
        }

        #region IList<T> Members

        public int IndexOf(T item)
        {
            return _impl.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            _impl.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            _impl.RemoveAt(index);
        }

        public T this[int index]
        {
            get
            {
                return _impl[index];
            }
            set
            {
                _impl[index] = value;
            }
        }

        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            _impl.Add(item);
        }

        public void Clear()
        {
            _impl.Clear();
        }

        public bool Contains(T item)
        {
            return _impl.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _impl.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _impl.Count; }
        }

        public bool IsReadOnly
        {
            get { return _impl.IsReadOnly; }
        }

        public bool Remove(T item)
        {
            return _impl.Remove(item);
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return _impl.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _impl.GetEnumerator();
        }

        #endregion
    }


    public interface IObservableList<T> : IList<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
    }

    public class ObservableListWrapper<T> : IObservableList<T>
    {

        #region Globals

        protected IList<T> _impl;
        /// <inheritdoc />
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        private readonly NGenerics.UI.SimpleMonitor monitor;



        /// <inheritdoc />
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Construction


        /// <inheritdoc />
        public ObservableListWrapper(IList<T> impl)
        {
            monitor = new NGenerics.UI.SimpleMonitor();
            _impl = impl;
        }

        public ObservableListWrapper() : this(Factory.NewList<T>())
        {
        }

        #endregion

        #region IList<T> Members

        public int IndexOf(T item)
        {
            return _impl.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            _impl.Insert(index, item);
            OnPropertyChanged("Count", "IsEmpty", "Item[]");
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        }

        public void RemoveAt(int index)
        {
            T item = this[index];
            _impl.RemoveAt(index);
            OnPropertyChanged("Count", "IsEmpty", "Item[]");
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
        }

        public T this[int index]
        {
            get
            {
                return _impl[index];
            }
            set
            {
                _impl[index] = value;
                OnPropertyChanged("Item[]");
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value));
            }
        }

        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            _impl.Add(item);
            OnPropertyChanged("Count", "IsEmpty", "Item[]");
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, Count-1));
        }

        public void Clear()
        {
            _impl.Clear();
            OnPropertyChanged("Count", "IsEmpty", "Item[]");
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public bool Contains(T item)
        {
            return _impl.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _impl.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _impl.Count; }
        }

        public bool IsReadOnly
        {
            get { return _impl.IsReadOnly; }
        }

        public bool Remove(T item)
        {
            int index = IndexOf(item);
            bool result = _impl.Remove(item);
            OnPropertyChanged("Count", "IsEmpty", "Item[]");
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
            return result;
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return _impl.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _impl.GetEnumerator();
        }

        #endregion

 
        /// <summary>
        /// Raises the <see cref="CollectionChanged"/> event.
        /// </summary>
        /// <param name="e">A <see cref="NotifyCollectionChangedAction"/> that contains the event data.</param>
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
            {
                using (BlockReentrancy())
                {
                    CollectionChanged(this, e);
                }
            }
        }

        /// <summary>
        /// Called when the specified properties change.
        /// </summary>
        /// <param name="propertyNames">The property names.</param>
        protected virtual void OnPropertyChanged(params string[] propertyNames)
        {
            for (int i = 0; i < propertyNames.Length; i++)
            {
                OnPropertyChanged(new PropertyChangedEventArgs(propertyNames[i]));
            }
        }

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event.
        /// </summary>
        /// <param name="e">A <see cref="PropertyChangedEventArgs"/> that contains the event data.</param>
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }

        /// <inheritdoc cref="ObservableCollection{T}.BlockReentrancy"/>
        protected IDisposable BlockReentrancy()
        {
            monitor.Enter();
            return monitor;
        }


        /// <inheritdoc cref="ObservableCollection{T}.CheckReentrancy"/>
        protected void CheckReentrancy()
        {
            if ((monitor.Busy && (CollectionChanged != null)) && (CollectionChanged.GetInvocationList().Length > 0))
            {
                throw new InvalidOperationException("re-entrancy not allowed.");
            }
        }
    }


  
    public interface IListView<T> : IEnumerable<T>
    {
        int Count{get;}
        T this[int index] {get;}
        event NotifyCollectionChangedEventHandler CollectionChanged;
    }

    public class ListView<T> : IListView<T>
    {
        protected IList<T> _impl;

        protected int _start;
        protected int _count;

        private NotifyCollectionChangedEventHandler _changed;

        public event NotifyCollectionChangedEventHandler CollectionChanged
        {
            add { _changed+=value;}
            remove { _changed-=value;}
        }

        public ListView(IList<T> impl, int start, int count)
        {
            _impl = impl;
            _start = start;
            _count = count;
        }

        public int Count{get{return _count<0?_impl.Count:_count;}}

        public T this[int index] { get { return _impl[index + _start]; } }

        public void Add(T value)
        {
            _impl.Add(value);
            FireChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value, _impl.Count-1));
        }

        public void Clear()
        {
            _impl.Clear();
            FireChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }


        public void FireChanged(NotifyCollectionChangedEventArgs args)
        {
            if (null != _changed)
                _changed(this, args);
        }
        #region IEnumerable<T> Members

        public IEnumerator<T>  GetEnumerator()
        {
 	        for(int i=_start;i<Count;i++)
                yield return _impl[i+_start];
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator  System.Collections.IEnumerable.GetEnumerator()
        {
 	        return (this as IEnumerable<T>).GetEnumerator();
        }

        #endregion
        }
}